<?php
declare(strict_types=1);

namespace WebApp\lib\util;

use App\Lib\SwoftUtil;
use Co;
use Exception;
use ErrorException;
use FilesystemIterator;
use Generator;
use ServerApp\lib\process_2\SProcess;
use Swlib\SaberGM;
use Throwable;
use txz\common\base\utils\LogHelper;
use txz\common\base\utils\MonitorHelper;
use txz\common\conf\InnerConfig;
use txz\common\dao\base\cache\CacheClientMgr;
use txz\common\dao\base\memcache\CMemCacheMgr;
use txz\common\third_lib\file\File;
use txz\common\third_lib\model\servers\_201\txz_user_version\VersionUser;
use WebApp\lib\db\Table;
use PDO;
use WebApp\lib\exception\ArgsEmpty;
use WebApp\lib\exception\ClientOkException;
use WebApp\lib\exception\ProcessAfter;
use WebApp\lib\exception\ProcessExecute;

/**
 * as
 * Class Util
 * @package txz\oss\oss\base
 */
class Util
{
    const ExcelRequirePath = "/data/web/server/common/third_lib/excel/base/PHPExcel/IOFactory.php";
    const FLOAT = 1000;
    const INT = 1001;
    const STR = 1002;

    public function is_torrent($v)
    {
        preg_match_all('/^[a-zA-z0-9]([a-zA-z0-9]{38})[a-zA-z0-9]$/', $v, $matches);
        $adfa = $matches[0][0] ?? '';
        return ($adfa && ($adfa === $v));
    }

    public static function mTime(int $time) {
        if ($time < 11111111111) {
            return $time * 1000;
        }
        return $time;
    }

    public static function timeBetween($start, $end) {
        $start_date = date("Y-m-d $start");
        $end_date = date("Y-m-d $end");
        $start_date_timestamp = strtotime($start_date);
        $end_date_timestamp = strtotime($end_date);

        $now = time();
        if ($start_date_timestamp <= $now && $now <= $end_date_timestamp) {
            return true;
        } else {
            return false;
        }
    }

    public static function numToWord($num)
    {
        $chiNum = array('零', '一', '二', '三', '四', '五', '六', '七', '八', '九');
        $chiUni = array('', '十', '百', '千', '万', '亿', '十', '百', '千');

        $num_str = (string)$num;

        $num_str_arr = explode('.', $num_str);
        if (count($num_str_arr) === 2) {
            $res = numToWord(intval($num_str_arr[0])) . '点';
            foreach (str_split($num_str_arr[1], 1) as $item) {
                $res .= $chiNum[intval($item)];
            }
            return $res;
        }

        $count = strlen($num_str);
        $last_flag = true; //上一个 是否为0
        $zero_flag = true; //是否第一个
        $temp_num = null; //临时数字

        $chiStr = '';//拼接结果
        if ($count == 2) {//两位数
            $temp_num = $num_str[0];
            $chiStr = $temp_num == 1 ? $chiUni[1] : $chiNum[$temp_num] . $chiUni[1];
            $temp_num = $num_str[1];
            $chiStr .= $temp_num == 0 ? '' : $chiNum[$temp_num];
        } else if ($count > 2) {
            $index = 0;
            for ($i = $count - 1; $i >= 0; $i--) {
                $temp_num = $num_str[$i];
                if ($temp_num == 0) {
                    if (!$zero_flag && !$last_flag) {
                        $chiStr = $chiNum[$temp_num] . $chiStr;
                        $last_flag = true;
                    }
                } else {
                    $chiStr = $chiNum[$temp_num] . $chiUni[$index % 9] . $chiStr;
                    $zero_flag = false;
                    $last_flag = false;
                }
                $index++;
            }
        } else {
            $chiStr = $chiNum[$num_str[0]];
        }
        return $chiStr;
    }

    public static function chinese2number($chNum) {
        $numArr = array(
            '零' => '0',
            '一' => '1',
            '二' => '2',
            '三' => '3',
            '四' => '4',
            '五' => '5',
            '六' => '6',
            '七' => '7',
            '八' => '8',
            '九' => '9',
        );
        $numUnit = array(
            '十' =>'10',
            '百' =>'100',
            '千' =>'1000',
            '万' =>'10000',
            '亿' =>'100000000',
        );
        $number = 0;
        for ($i = 0;$i<=mb_strlen($chNum); $i++) {
            $str = mb_substr($chNum,$i,2);
            $first = mb_substr($str,0,1);
            $second = mb_substr($str,1,1);
            if (isset($numUnit[$first])){
                if (!$number) {
                    $number = 1;
                }
                $number *= $numUnit[$first];
            } else if (isset($numArr[$first])) {
                $i++;
                if (isset($numArr[$second])) {
                    if ($numArr[$first] == 0) {
                        $i--;
                    }else{
                        $number += intval($numArr[$first].$numArr[$second]);
                    }
                } else if(isset($numUnit[$second])) {
                    $number += intval($numArr[$first]*$numUnit[$second]);
                } else{
                    $number += intval($numArr[$first]);
                }
            }

        }
        return $number;
    }

    public static function arrClearNull(&$arr)
    {
        if (is_array($arr)) {
            foreach ($arr as $key => $val) {
                if ($val === null) {
                    unset($arr[$key]);
                }
            }
        } else {
            foreach ($arr as $key => $val) {
                if ($val === null) {
                    unset($arr->$key);
                }
            }
        }
    }

    /**
     * @param $arr
     * @throws ArgsEmpty
     */
    public static function check_args_not_empty($arr) {
        $flag = true;
        foreach ($arr as $key => $item) {
            if ($item === null) {
                $flag = false;
                break;
            }
        }
        if (!$flag) {
            throw new ArgsEmpty();
        }
    }

    public static function once(callable $func) {
        $arr = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
        $file = $arr[0]['file'];
        $line = $arr[0]['line'];
        $key = "$file-{$line}";
        $val = Util::get_global($key);
        if (!$val) {
            Util::set_global($key, 1);
            try {
                $func();
            } catch (Throwable $e) {
                var_dump($e);
            }
        }
    }

    public static function requirePHPExcel() {
        require_once (self::ExcelRequirePath . '');
    }

    public static function buildArrByRows($rows, $key) {
        return array_map(function($row)use($key){
            return $row[$key];
        }, $rows);
    }

    public static function falseZero(&$val) {
        $val = $val ?: 0;
    }

    public static function round($num): string {
        if ($num * 100 % 100 === 0) {
            return intval($num) . ".00";
        }
        return round($num, 2) . '';
    }

    public static function explodeStr($str) {
        $str_length = strlen($str);
        $arr = [];
        for ($i=0; $i<$str_length; $i++) {
            $arr[] = $str[$i];
        }
        return $arr;
    }

    public static function isBackInline() {
        return \txz\conf\CommonConfig::$test == TXZ_ENV_BACK_INLINE;
    }

    public static function isFipOnline() {
        return \txz\conf\CommonConfig::$test == TXZ_ENV_FIP_ONLINE;
    }

    public static function countLimitWithTime($title, $uid, $start_time, $end_time, $max_count, $min_count = -1, &$cb=null) {
        $start_time = self::getTimestamp($start_time);
        $end_time = self::getTimestamp($end_time);
        $event_time = time();
        if (!($start_time < $event_time && $event_time < $end_time)) { //event time 不在判断区间
            Util::logD(__LINE__);
            return false;
        }

        $redisClient = CacheClientMgr::getCacheClient(InnerConfig::$redis_server);

        $redis_key = $redisClient->genKey($title, $uid, $start_time, $end_time);
        $all_count = (int)$redisClient->getValue($redis_key);
        Util::logD("redis val($all_count) config max val($max_count) config min val($min_count)");
        if (!($min_count < $all_count && $all_count < $max_count)) {
            Util::logD(__LINE__);
            return false;
        }

        if ($cb === null) {
            $cb = function() use($redisClient, $redis_key, $end_time, $event_time) {
                $redisClient->increment($redis_key, 1, 0, ($end_time - $event_time));
            };
        } else {
            $cb_o = clone $cb;
            $cb = function() use($redisClient, $redis_key, $end_time, $event_time, $cb_o) {
                $redisClient->increment($redis_key, 1, 0, ($end_time - $event_time));
                if (is_callable($cb_o)) {
                    $cb_o();
                }
            };
        }

        return true;
    }

    public static function unsignedInt(&$arr, $key) {
        $val = $arr[$key] ?? 0;
        if ($val < 0) {
            $val = 0;
        }
        return $val;
    }

    public static function json_values($obj) {
        $arr = [];
        foreach ($obj as $key => $val) {
            $arr[] = $val;
        }
        return $arr;
    }

    public static function isValidIdCard($num) {
        //350103199310103254
        function idcard_verify_number($idcard_base){
            if (strlen($idcard_base) != 17){
                return false;
            }
            $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); //debug 加权因子
            $verify_number_list = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); //debug 校验码对应值
            $checksum = 0;
            for ($i = 0; $i < strlen($idcard_base); $i++){
                $checksum += substr($idcard_base, $i, 1) * $factor[$i];
            }
            $mod = $checksum % 11;
            $verify_number = $verify_number_list[$mod];
            return $verify_number;
        }
        /*/
        # 函数功能：将15位身份证升级到18位
        # 函数名称：idcard_15to18
        # 参数表 ：string $idcard 十五位身份证号码
        # 返回值 ：string
        # 更新时间：Fri Mar 28 09:49:13 CST 2008
        /*/
        function idcard_15to18($idcard){
            if (strlen($idcard) != 15){
                return false;
            }else{// 如果身份证顺序码是996 997 998 999，这些是为百岁以上老人的特殊编码
                if (array_search(substr($idcard, 12, 3), array('996', '997', '998', '999')) !== false){
                    $idcard = substr($idcard, 0, 6) . '18'. substr($idcard, 6, 9);
                }else{
                    $idcard = substr($idcard, 0, 6) . '19'. substr($idcard, 6, 9);
                }
            }
            $idcard = $idcard . idcard_verify_number($idcard);
            return $idcard;
        }
        /*/
        # 函数功能：18位身份证校验码有效性检查
        # 函数名称：idcard_checksum18
        # 参数表 ：string $idcard 十八位身份证号码
        # 返回值 ：bool
        # 更新时间：Fri Mar 28 09:48:36 CST 2008
        /*/
        function idcard_checksum18($idcard){
            if (strlen($idcard) != 18){ return false; }
            $idcard_base = substr($idcard, 0, 17);
            Util::logD(strtoupper(substr($idcard, 17, 1)));
            Util::logD("_".idcard_verify_number($idcard_base));
            if (idcard_verify_number($idcard_base) != strtoupper(substr($idcard, 17, 1))){
                return false;
            }else{
                return true;
            }
        }
        /*/
        # 函数功能：身份证号码检查接口函数
        # 函数名称：check_id
        # 参数表 ：string $idcard 身份证号码
        # 返回值 ：bool 是否正确
        # 更新时间：Fri Mar 28 09:47:43 CST 2008
        /*/
        function check_id($idcard)
        {
            if (strlen($idcard) == 15 || strlen($idcard) == 18) {
                if (strlen($idcard) == 15) {
                    $idcard = idcard_15to18($idcard);
                }

                if (idcard_checksum18($idcard)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        return check_id($num);
    }

    public static function timeDiffIsOver($time1, $time2, $limit) {
        $time1 = self::getTimestamp($time1);
        $time2 = self::getTimestamp($time2);
        $diff = abs($time1 - $time2);
        if ($diff > $limit) {
            return true;
        } else {
            return false;
        }
    }

    private static function getParamsByMsg($msg) {
        if ($msg === ClientOkException::KEY) {
            $params = $GLOBALS[ClientOkException::KEY];
        } else {
            $params = $msg;
        }
        return $params;
    }

    public static function process($before, $exec, $after=null) {
        try {
            return $before();
        } catch (ProcessExecute $e) {
            try {
                return $exec(self::getParamsByMsg($e->getMessage()));
            } catch (ProcessAfter $e) {
                if (is_callable($after)) {
                    return $after(self::getParamsByMsg($e->getMessage()));
                }
            }
        } catch (ProcessAfter $e) {
            if (is_callable($after)) {
                return $after(self::getParamsByMsg($e->getMessage()));
            }
        }
        return null;
    }

    public static function getDbDotTable(Table $table) {
        return $table->getDbName().'.'.$table->getTableName();
    }

    public static function timeDiffOver($update_time, int $over_flow_second) {
        return self::timeDiffIsOver(time(), $update_time, $over_flow_second);
    }

    public static function ContinueExecute(int $count = 1, callable $callback = null, ...$args): string
    {
        if (!$callback) return "";
        if (!is_numeric($count)) return "";
        for ($i=0; $i<$count; $i++) {
            try {
                $res = $callback(...$args);
            } catch (\Throwable $e) {
                $res = "";
            }

            if (!$res) {
                continue;
            }

            return $res;
        }

        return "";
    }

    public static function cleanDbConn() {
        $key = 'last_cleanDbConn_time-'.Util::get_pid();
        $last_time = $GLOBALS[$key] ?? 0;
        if (!Util::timeDiffOver($last_time, 3600)) {
            return;
        }
        swoftUtil::clean();
        $GLOBALS[$key] = time();
    }

    public static function get_pid(): int
    {
//        $header = json_decode(json_encode($GLOBALS['request']->header), true);
//        $uid = (int)$header["conn-uid"];
//        if (!$uid) return [];
        $pid = $GLOBALS['swoole_pid'] ?? $GLOBALS['pid'] ?? getmypid();
        return $pid;
    }

    public static function opFlag(int &$num, int $position, int $op){
        $op_num = pow(2, $position-1);
        if ($op) {
            $num = $op_num | $num;
        } else {
            $left = $num >> $position;
            $right = (pow(2, $position) - 1) & $num;
            $right = $right & (pow(2, $position-1)-1);
            $num = ($left << $position) | $right;
        }
    }

    public static function getOp(int &$num, int $position){
        $right = (pow(2, $position) - 1) & $num;
        return $right >> ($position-1);
    }

    public static function arrayExclude(array $main_array, array $check_array){
        $main_array_dict = [];
        foreach ($main_array as $main_item) {
            $main_array_dict[$main_item] = 1;
        }

        foreach ($check_array as $check_item) {
            if (isset($main_array_dict[$check_item])) {
                unset($main_array_dict[$check_item]);
            }
        }

        return array_keys($main_array_dict);
    }

    public static function addKeyValue($obj, &$key_name, $value) {
        $has_val = false;
        foreach ($obj as $key=> $val) {
            if ($val === $value) {
                $key_name = $key;
                return;
            }
        }
        if (!$has_val) {
            $obj->$key_name = $value;
        }
    }

    public static function buildDict($rows, string $key_name)
    {
        $dict = [];
        if (!$rows) return $dict;
        foreach ($rows as $row) {
            $dict[$row[$key_name]] = $row;
        }
        return $dict;
    }

    public static function splitFile(string $path, int $level = 100000) {
        $index = 0;
        $file_index = 0;
        $fp = fopen($path . "_$file_index", "w");
        foreach (Util::readLine($path) as $line) {
            fwrite($fp, "$line\n");
            $index++;
            if ($index > $level) {
                $index = 0;
                $file_index++;
                fclose($fp);
                $fp = fopen($path . "_$file_index", "w");
            }
        }
        fclose($fp);
    }

    /**
     * @param string $key_name
     * @param int $type
     * @param bool $must
     * @return int
     * @throws ErrorException
     */
    public static function request_post(string $key_name, int $type = PDO::PARAM_INT, bool $must = false)
    {
        if (defined("DEBUG") && DEBUG && !$_POST) {
            $params = &$_GET;
        } else {
            $params = &$_POST;
        }

        return self::request($params, $key_name, $type, $must);
    }

    /**
     * @param string $key_name
     * @param int $type
     * @param bool $must
     * @param string $msg
     * @return int|string|null
     * @throws ErrorException
     */
    public static function request_get(string $key_name, int $type = PDO::PARAM_INT, bool $must = false, $msg = null)
    {
        return self::request($_GET, $key_name, $type, $must, $msg);
    }

    /**
     * @param $param
     * @param string $key_name
     * @param int $type
     * @param bool $must
     * @param null $msg
     * @return int|string|null
     * @throws ErrorException
     */
    public static function request($param, string $key_name, int $type, bool $must = false, $msg = null)
    {
        if (is_array($param)) {
            if (!isset($param[$key_name])) {
                if ($must) throw new ErrorException($msg ?? "invalid param key $key_name"); else return null;
            } else {
                $val = $param[$key_name];
            }
        } else {
            if (!isset($param->$key_name)) {
                if ($must) throw new ErrorException($msg ?? "invalid param key $key_name"); else return null;
            } else {
                $val = $param->$key_name;
            }
        }

        switch ($type) {
            case MYSQLI_TYPE_LONG:
                $res = (int)($val);
                if ($res > 2147483647) {
                    throw new ErrorException($msg ?? "$key_name size overflow");
                }
                return $res;
                break;
            case Util::INT:
            case PDO::PARAM_INT:
                return (int)($val);
                break;
            case Util::STR:
            case PDO::PARAM_STR:
                return (string)($val);
                break;
            case Util::FLOAT:
            case Util::FLOAT:
                return (float)($val);
                break;
            default:
                self::logW("unknown type = $type");
                return (string)($val);
                break;
        }
    }

    public static function check_phone($phone)
    {
        $check = '/^(1(([35789][0-9])|(47)))\d{8}$/';
        if (preg_match($check, (string)$phone)) {
            return true;
        } else {
            return false;
        }
    }

    public static function splitInt(string $sp, string $str)
    {
        return array_map("intval", explode($sp, $str));
    }

    public static function deleteGlobalCache($key) {
        if (!is_string($key)) {
            $key = json_encode($key);
        }
        self::delete_global($key);
    }

    public static function getGlobalCache($key, callable $func, ...$param) :array {
        if (!is_string($key)) {
            $key = json_encode($key);
        }
        $row = self::get_global($key);
        if (!$row) {
            $row = $func(...$param);
            if ($row) {
                self::set_global($key, $row);
            }
        }
        if (!$row) {
            $row = [];
        }
        return $row;
    }

    public static function getCache($uid, string $cache_key, $time, callable $func, ...$param): array
    {
        $has_cache = false;
        return self::getCacheV2($has_cache, $uid, $cache_key, $time, $func, ...$param);
    }

    /**
     * @param bool $has_cache
     * @param $uid
     * @param string $cache_key
     * @param $time
     * @param callable $func
     * @param mixed ...$param
     * @return array
     */
    public static function getCacheV2(bool &$has_cache, $uid, string $cache_key, $time, callable $func, ...$param): array
    {
        $cache_name = "$cache_key-$uid";
        $mem_key = md5("$cache_name|" . json_encode($param));
        $memcache = CacheClientMgr::getCacheClient(InnerConfig::$redis_server);
        $str = $memcache->getValue($mem_key);
        if (!$str) {
            $has_cache = false;
            $row = $func(...$param);
            if ($row) {
                if (!is_numeric($time)) {
                    $time = 0;
                    if (!isset($row["$time"])) {
                        Util::logI(var_export($row, true));
                    } else {
                        $time = $row["$time"];
                    }
                    $time = $time - 100;
                }
                $memcache->setValue($mem_key, json_encode($row), $time);
            }
        } else {
            $has_cache = true;
            $row = self::jsonDecode($str);
        }
        if (!$row) {
            $row = [];
        }
        return $row;
    }

    public static function envIsInline(): bool
    {
        return (\txz\conf\CommonConfig::$test == TXZ_ENV_BACK_INLINE || \txz\conf\CommonConfig::$test == TXZ_ENV_FIP_INLINE);
    }

    public static function evnIsOnline(): bool
    {
        return (\txz\conf\CommonConfig::$test == TXZ_ENV_BACK_ONLINE || \txz\conf\CommonConfig::$test == TXZ_ENV_FIP_ONLINE);
    }

    /**
     * @param int $flag_num
     * @param Table $table
     * @param string $key
     */
    public static function multiTmpInsert(int $flag_num, Table $table, string $key): void
    {
        if (!isset($GLOBALS["$key-count"])) $GLOBALS["$key-count"] = 0;
        $GLOBALS["$key-count"]++;
        if ($GLOBALS["$key-count"] > $flag_num) {
//            Util::logD("multiTmpInsert, key=$key, count=$flag_num");
            Util::multiInsert($flag_num, $table, $GLOBALS[$key]);
            $GLOBALS["$key-count"] = 0;
            $GLOBALS[$key] = [];
        }
    }

    /**
     * @param int $flag_num
     * @param Table $table
     * @param string $id
     * @param string $key
     */
    public static function multiTmpUpdate(int $flag_num, Table $table, string $id, string $key): void
    {
        if (!isset($GLOBALS["$key-count"])) $GLOBALS["$key-count"] = 0;
        $GLOBALS["$key-count"]++;
        if ($GLOBALS["$key-count"] > $flag_num) {
//            Util::logD("multiTmpUpdate, key=$key, count=$flag_num");
//            Util::logJson($GLOBALS[$key], "update_data=");
            Util::multiUpdate($flag_num, $table, $GLOBALS[$key], $id);
            $GLOBALS["$key-count"] = 0;
            $GLOBALS[$key] = [];
        }
    }

    public static function jsonDecode($str): array
    {
        if (is_array($str)) return $str;
        if (is_numeric($str)) return [];
        if (!$str) return [];
        try {
            $arr = json_decode(($str), true);
            if (!$arr) {
                throw new ErrorException();
            }
        } catch (Throwable $e) {
            $arr = self::jsonDecodeStrips($str);
        }

        return $arr ?: [];
    }

    public static function jsonDecodeStrips($str): array
    {
        if (!$str) return [];
        try {
            $arr = json_decode(stripslashes($str), true);
        } catch (Throwable $e) {
            Util::logObj($e->getMessage());
            return [];
        }

        return $arr ?: [];
    }

    public static function jsonDecodeObj($str)
    {
        if (!$str) return null;
        try {
            return json_decode($str);
        } catch (Throwable $e) {
            return null;
        }
    }

    public static function changeBigId(string $sid): int
    {
        return (int)(hexdec(substr(md5($sid), 8, 14)));
    }

    public static function changeId(string $sid): int
    {
        return (int)(hexdec(substr(md5($sid), 8, 7)));
    }

    public static function cache(&$set, $id, $val = null)
    {
        if (isset($set[$id])) {
            return $set[$id];
        } else {
            if ($val !== null) {
                $set[$id] = $val;

                return $val;
            } else {
                return null;
            }
        }
    }

    public static function checkTimeOverLap(array $rows, $select, $start, $end): bool
    {
        try {
            if ($end <= $start) throw new ErrorException('0');
            array_map(function ($row) use ($select, &$start, &$end) {
                self::splitRow($row, $select);
                if ($start <= $select->start && $select->end <= $end) throw new ErrorException('1');
                if ($select->start <= $start && $end <= $select->end) throw new ErrorException('2');
                if ($start <= $select->start && $select->start <= $end && $end <= $select->end) throw new ErrorException('3');
                if ($select->start <= $start && $start <= $select->end && $select->end <= $end) throw new ErrorException('4');
            }, $rows);
        } catch (Throwable $e) {
            Util::logW("checkTimeOverLap=" . $e->getMessage());
            Util::logW("checkTimeOverLap=select=" . json_encode($select));
            Util::logW("checkTimeOverLap=rows=" . json_encode($rows));
            Util::logW("checkTimeOverLap=start=" . json_encode($start));
            Util::logW("checkTimeOverLap=end=" . json_encode($end));
            return false;
        }
        return true;
    }

    public static function chooseTrue(...$params)
    {
        $item = '';
        foreach ($params as $item) if ($item) return $item;
        return $item ? $item : null;
    }

    public static function batchDataMaker(array &$multi_update, $id, array $update): void
    {
        if ($update) foreach ($update as $key => $val) $multi_update[$key][$id] = $val;
    }

    public static function splitRow(array &$row, &$need_fields): void
    {
        if (!$row) {
            $need_fields = null;
        } else {
            $new = [];
            foreach ($need_fields as $key => $val) {
                $new[$key] = $row[$val];
            }
            $need_fields = json_decode(json_encode($new));
        }
    }

    public static function makeSelectResult(&$need_fields, string $str): string
    {
        $need_fields_dict = json_decode(json_encode($need_fields), true);
        $params = array_values($need_fields_dict);
        if (is_array($params[0])) {
            $str = str_replace("*", substr(str_repeat('%s,', count($params)), 0, -1), $str);
            $need_fields_key_arr = array_keys($need_fields_dict);
            foreach ($params as $index => $param) {
                $tmp_param = "$param[0].`$param[1]`";
                if (isset($param[2]))
                    $tmp_param .= " as $param[3] ";
                $need_fields_dict[$need_fields_key_arr[$index]] = $param[1];
                $params[$index] = $tmp_param;
            }
            $need_fields = json_decode(json_encode($need_fields_dict));
        } else {
            foreach ($params as &$val) {
                if (!Util::hasString($val, " ") && !Util::hasString($val, ".")) {
                    $val = "`$val`";
                }
            }
            $str = str_replace("*", substr(str_repeat('%s,', count($params)), 0, -1), $str);
        }
        return sprintf($str, ...$params);
    }

    public static function multiRead($num, Table $table, $select, &$multi_filter, $has, $has_not)
    {

    }

    public static function multiUpdate(int $num, Table $table, array &$multi_update, string $primary_name): void
    {
        if (!$multi_update) return;

        $db_name = $table->getDbName();
        $table_name = $table->getTableName();
        $o_sql = "UPDATE `$db_name`.`$table_name` SET ";

        $exec = function ($tmp, $key, $primary_name, $key_set) use (&$o_sql, &$table) {
            if ($tmp) {
                $sql = $o_sql . "`$key` = CASE $primary_name $tmp END, ";
                $id_str = join(',', array_keys($key_set));
                $sql = substr($sql, 0, -2) . " WHERE $primary_name IN ($id_str)";
                if (false === $table->execute($sql)) {
                    Util::logW($sql);
                }
            }
        };

        foreach ($multi_update as $key => $obj) {
            $tmp = "";
            $key_set = [];
            $count = 0;
            foreach ($obj as $primary => $val) {
                if (!isset($key_set[$primary])) $key_set[$primary] = true;
                if (!is_numeric($val)) {
                    $val = addslashes($val);
                    $val = "'$val'";
                }
                $tmp .= " WHEN $primary THEN $val";
                $count++;
                if ($count >= $num) {
                    $exec($tmp, $key, $primary_name, $key_set);
                    $tmp = "";
                    $key_set = [];
                    $count = 0;
                }
            }
            if ($count > 0) {
                $exec($tmp, $key, $primary_name, $key_set);
            }
        }

        $multi_update = [];
    }

    /**
     * @param int $num
     * @param Table $table
     * @param array $add_data_arr
     * @param bool $is_insert
     */
    public static function multiInsert(int $num, Table $table, array &$add_data_arr, bool $is_insert = true): void
    {
        if (!$add_data_arr) return;

        if (!is_array($add_data_arr[0] ?? null)) {
            Util::logObj($add_data_arr);
            return;
        }

        $db_name = $table->getDbName();
        $table_name = $table->getTableName();
        $str = $is_insert ? 'INSERT' : 'REPLACE';
        $o_sql = "$str ignore INTO `$db_name`.`$table_name` (";
        $param_name_num = 0;
        foreach ($add_data_arr[0] as $param_name => $useless) {
            $param_name_num++;
            $o_sql .= "`$param_name`,";
        }
        if (!$param_name_num) return;
        $o_sql = substr($o_sql, 0, -1) . ") values ";

        $exec = function (string $sql, int $count) use (&$table) {
            if (false === $table->execute(substr($sql, 0, -1))) {
                Util::logW($sql);
            } else {
                unset($count);
                //Util::logD("multi insert ($count)");
            }
//            else
        };

        $sql = $o_sql;
        $count = 0;
        foreach ($add_data_arr as $add_data) {
            $str = "(";
            foreach ($add_data as $val) {
                if (!is_numeric($val)) {
                    if ($val === null) {
                        $val = "''";
                    } else {
                        $val = addslashes($val);
                        $val = "'$val'";
                    }
                }
                $str .= "$val,";
            }
            $str = substr($str, 0, -1) . "),";
            $sql .= $str;
            $count++;
            if ($count >= $num) {
                $exec($sql, $count);
                $sql = $o_sql;
                $count = 0;
            }
        }
        if ($count > 0) $exec($sql, $count);

        $add_data_arr = [];
    }

    /**
     * @param $arr
     * @param $func
     * @return array
     */
    public static function arr_filter($arr, $func): array
    {
        return array_values(array_filter($arr, $func));
    }

    public static function arr_filter_not_null($arr): array
    {
        return array_values(array_filter($arr, function($row){
            return $row !== null;
        }));
    }

    /**
     * Util constructor.
     */
    private function __construct()
    {
    }

    /**
     * @param $path1
     * @param $file_name
     * @param $content
     * @return string
     * @throws ErrorException
     */
    public static function uploadContentToOss(string $path1, string $file_name, string $content): string
    {
        $image_dir = "/data/web/txz_static/$path1";
        $path = $image_dir . $file_name;

        if (Util::hostIsTest()) {
            $url = "https://oss.txzing.com/tools/UpMp3?path=$path";
            $res = Util::httpCurl($url, [
                "content" => $content
            ]);
            if (!$res) return '';

            $url = "http://static.txzing.com/$path1$file_name?t=" . time();
            return $url;
        } else {
            if (!is_dir($image_dir)) {
                mkdir($image_dir, 0766, true);
            }

            if (!is_dir($image_dir)) {
                self::e("mkdir err " . __LINE__);
            }

            if (false === strpos($path, 'txz_static')) self::e("no txz_static");

            if (file_put_contents($path, $content)) {
                if (false == File::syncStaticFile($path, File::STATIC_BUCKET, true, true)) {
                    Util::deleteFile($path);
                    self::e("upload err " . __LINE__);
                }

                $url = "http://static.txzing.com/$path1$file_name?t=" . time();
                Util::deleteFile($path);
                return $url;
            } else {
                Util::deleteFile($path);
                self::e("content err2");
            }
        }

        return '';
    }

    /**
     * @param array $update
     * @param array $row
     * @param string $key
     * @param $duration
     */
    public static function fillUpdateNotEmpty(&$update, &$row, $key, $duration): void //修改不相同的
    {
        if (!$key) {
            return;
        }
        if (!array_key_exists($key, $row)) {
            return;
        }
        if ($duration && ($row[$key] != $duration)) {
            $update[$key] = $duration;
        }
    }

    /**
     * @param array $update
     * @param array $row
     * @param string $key
     * @param $duration
     */
    public static function fillUpdate(&$update, &$row, $key, $duration): void //修改不相同的
    {
        if (!$key) {
            return;
        }
        if (!array_key_exists($key, $row)) {
            return;
        }
        if (($duration !== null) && ($row[$key] != $duration)) {
            $update[$key] = $duration;
        }
    }

    /**
     * @param array $update
     * @param array $row
     * @param string $key
     * @param $duration
     */
    public static function fillUpdate2(array &$update, array &$row, string $key, $duration): void //只填充 不修改
    {
        if ($duration && !($row[$key] ?? '')) {
            $update[$key] = $duration;
        }
    }

    /**
     * @param string $name
     * @return string
     */
    public static function html_parser(string $name): string
    {
        preg_match_all("/\&\#(.*)\;/U", $name, $arr);
        if (isset($arr[0]) && $arr[0]) {
            $arr[0] = array_unique($arr[0]);
            foreach ($arr[0] as $filter) {
                try {
                    $str1 = shell_exec("python2 /data/web/server/oss/tools/util/tools/html_parser.py '$filter'");
                    $str2 = substr($str1, 0, -1);
                    $str3 = base64_decode($str2);
                    $name = str_replace($filter, $str3, $name);
                } catch (Throwable $e) {
                    Util::logObj([
                        $str1 ?? '',
                        $str2 ?? '',
                        $str3 ?? '',
                        $name ?? '',
                        $filter,
                    ]);
                    sleep(10);
                }
            }
        }
        return $name;
    }

    /**
     * @param string $process_name
     */
    public static function onlyOneProgress(string $process_name): void
    {
        $grep_str = $process_name;

        $my_pid = Util::get_pid();
        for ($i = 0; $i < 10; $i++) {
            $cmd = "ps -A -o pid,etime,args | grep '$grep_str' | grep -v grep | grep -v $my_pid";

            $output = shell_exec($cmd);
            if (!$output) {
                break;
            }
            $line = trim($output);
            $line_arr = explode(' ', $line);
            $time_info = $line_arr[1];
            $time_info_arr = explode('-', $time_info);
            if (count($time_info_arr) >= 2) {
                $day = (int)$time_info_arr[0];
                Util::logI("day = $day");
                SProcess::kill((int)$line_arr[0], 9);
                continue;
            } else {
                echo("has another program running\n");
                exit;
            }
        }
    }

    /**
     * @param int $count
     * @return array
     */
    public static function randomBytes(int $count): array
    {
        $arr = ['a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        $buffer = [];
        for ($i = 0; $i < $count; $i++) {
            $buffer[] = $arr[array_rand($arr, 1)] . $arr[array_rand($arr, 1)];
        }

        return $buffer;
    }

    /**
     * @param string $str
     * @return array
     */
    public static function BufferFrom(string $str): array
    {
        return str_split(bin2hex($str), 2);
    }

    /**
     * @param $arr
     */
    public static function BufferPrint($arr): void
    {
        echo join(" ", $arr) . "\n";
    }

    /**
     * @param Table $mysql_table
     * @param array $primary_key
     * @param int $page_size
     * @param callable $where_maker
     * @param bool $random
     * @return Generator
     */
    public static function tableReader(Table $mysql_table, array $primary_key, int $page_size, &$where_maker, $random=false): Generator
    {
        $need_name = $primary_key[0]['name'];

        $where = [];
        foreach ($primary_key as $item) {
            $where[$item['name']] = $item['condition'];
        }

        $max_row = $mysql_table->getObj([
            $need_name
        ], $where, '', $need_name);

        $min_row = $mysql_table->getObj([
            $need_name
        ], $where, '', [$need_name]);

        $max_id = intval($max_row[$need_name] ?? 0) + 1;
        $min_id = intval($min_row[$need_name] ?? 0);

        $fen = (int)ceil(($max_id - $min_id) / $page_size);

        $where_maker = function ($offset) use ($min_id, $need_name, $page_size, $primary_key) {
            $min_num = intval($offset) + $min_id;
            $max_num = $min_num + $page_size;

            $where_str = "";
            foreach ($primary_key as $index => $item) {
                if ($index == 0) {
                    continue;
                }
                $tmp_0 = $item['condition'][0];
                $tmp_1 = $item['condition'][1];
                switch ($tmp_0) {
                    case "between":
                        if (is_array($tmp_1)) {
                            $tmp_1 = $tmp_1[0] . " and " . $tmp_1[1];
                        }
                        break;
                    case "in":
                        if (is_array($tmp_1)) {
                            $tmp_1 = "(" . join(",", $tmp_1) . ")";
                        }
                        break;
                }
                $where_str .= "(" . $item['name'] . " " . $tmp_0 . " " . $tmp_1 . ") and ";
            }

            return $where_str . "$need_name>=$min_num and $need_name<$max_num";
        };

        $page_arr = range(1, $fen);
        if ($random) {
            shuffle($page_arr);
        }
        foreach ($page_arr as $i) {
            $offset = ($i - 1) * $page_size;
            yield $offset;
        }
    }

    /**
     * @param $logo
     * @param $path1
     * @param $file_name
     * @return string
     * @throws ErrorException
     */
    public static function uploadBase64Content($logo, $path1, $file_name): string
    {
        $image_dir = "/data/web/txz_static/$path1";
        $path = $image_dir . $file_name;

        if (Util::hostIsTest()) {
            $url = "https://oss.txzing.com/tools/UpImg";
            $res = Util::httpCurl($url, [
                "path" => $path,
                "data" => $logo,
            ]);
            if ($res && Util::startWith($res, "/data/web"))
                return "https://static.txzing.com/$path1$file_name?t=" . time();

            self::e("upload err " . __LINE__);
        } else {
            if (Util::startWith($logo, '/')) $logo = substr($logo, 0, -1);

            $base64_image_content = $logo;

            if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)) {
                $image_dir = "/data/web/txz_static/$path1";

                if (!is_dir($image_dir)) {
                    exec("mkdir -p $image_dir");
                }
                $path = $image_dir . $file_name;

                if (false === strpos($path, 'txz_static')) self::e("no txz_static");

                if (file_put_contents($path, base64_decode(str_replace($result[1], '', $base64_image_content)))) {
                    if (false == File::syncStaticFile($path, File::STATIC_BUCKET, true, true)) {
                        self::deleteFile($path);
                        self::e("upload content err");
                    }

                    $url = "https://static.txzing.com/$path1$file_name?t=" . time();
                    self::deleteFile($path);
                    return $url;
                } else {
                    self::e("base64 content err2");
                }
            } else {
                self::e("base64 content err1");
            }
        }

        return '';
    }

    public static function check_dir($dir)
    {
        if (!is_dir($dir)) {
            exec("mkdir -p $dir");
        }
    }

    public static function numberSimpleToChinese($num)
    {
        $num = str_replace('0', '零', $num);
        $num = str_replace('1', '一', $num);
        $num = str_replace('2', '二', $num);
        $num = str_replace('3', '三', $num);
        $num = str_replace('4', '四', $num);
        $num = str_replace('5', '五', $num);
        $num = str_replace('6', '六', $num);
        $num = str_replace('7', '七', $num);
        $num = str_replace('8', '八', $num);
        $num = str_replace('9', '九', $num);

        return $num;
    }

    public static function getTtVersion($uid)
    {
        $res = VersionUser::getInstance($uid)->getVersion('com.txznet.music');
        if (!$res) {
            return 0;
        }
        list($a, $b, $c) = array_map(function ($val) {
            return sprintf('%04d', intval($val) * 1000);
        }, explode('.', $res));
        $tt_version = intval($a . $b . $c);
        return $tt_version;
    }

    private static function contactMessage($tag, $why = '')
    {
        return $tag . "<===>" . (!is_string($why) ? json_encode($why, JSON_UNESCAPED_UNICODE) : $why);
    }

    public static function monitorF($tag, $why = '')
    {
        $tag = preg_replace('/\s/', '_', $tag);
        $message = self::contactMessage($tag, $why);
        self::logObj(debug_backtrace());
        self::logW($message, 1);
        MonitorHelper::monitorFatal($tag);
    }

    /**
     * @param string $tag
     * @param string $why
     * @param null $level
     */
    public static function monitorE(string $tag, $why = '', $level=null)
    {
        $tag = preg_replace('/\s/', '_', $tag);
        $message = self::contactMessage($tag, $why);
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
        if (is_string($backtrace)) {
            $debug_trace = $backtrace;
        } else {
            $debug_trace = var_export($backtrace, true);
        }

        self::logW($message, $level);
        self::logW($tag . "@" . $debug_trace, $level);
        MonitorHelper::monitorError($tag);
    }

    /**
     * @param string $tag
     * @param string $why
     * @param bool $echo
     */
    public static function monitorI(string $tag, $why = '', $echo = true)
    {
        $tag = preg_replace('/\s/', '_', $tag);
        $message = self::contactMessage($tag, $why);
        if ($why) {
            self::logI($message, 1, $echo);
        }
        MonitorHelper::monitorNormal($tag);
    }

    public static function my_dir($dir)
    {
        $files = array();
        if (@$handle = opendir($dir)) { //注意这里要加一个@，不然会有warning错误提示：）
            while (($file = readdir($handle)) !== false) {
                if ($file != ".." && $file != ".") { //排除根目录；
                    if (is_dir($dir . "/" . $file)) { //如果是子文件夹，就进行递归
                        $files[$file] = self::my_dir($dir . "/" . $file);
                    } else { //不然就将文件的名字存入数组；
                        $files[] = $file;
                    }
                }
            }
            closedir($handle);
            return $files;
        }
        return $files;
    }

    public static function get_global($key_name, $default = null)
    {
        return $GLOBALS[$key_name . '_@' . getmypid()] ?? $default;
    }

    public static function set_global($key_name, $value = null)
    {
        $GLOBALS[$key_name . '_@' . getmypid()] = $value;
    }

    public static function delete_global($key_name)
    {
        unset($GLOBALS[$key_name . '_@' . getmypid()]);
        $GLOBALS[$key_name . '_@' . getmypid()] = null;
    }

    public static function go($num, $for, $func)
    {
        pcntl_signal(SIGCHLD, SIG_IGN);
        $process = new SProcess(function (SProcess $process) use (&$num, &$for, &$func) {
            Util::set_global("is_co", 1);
            $GLOBALS['process'] = &$process;
            $GLOBALS['last_time'] = time();

            swoole_event_add($GLOBALS['process']->pipe, function () {
                $data = $GLOBALS['process']->read();
                if ($data === "\t") {
                    $GLOBALS['last_time'] = time();
                    $GLOBALS['process']->write("\t");
                }
            });

            $tid = swoole_timer_tick(5000, function () {
                if (time() - $GLOBALS['last_time'] > 300) {
                    $GLOBALS['process']->write("\e");
                    $GLOBALS['process']->write("\e");
                    $GLOBALS['process']->write("\e");
                    SProcess::kill($GLOBALS['process']->pid, 9);
                    exit;
                }
            });

            go(function () use (&$num, &$for, &$func, $tid) {
                (new class($num, $for, $func)
                {
                    private $for;
                    /**
                     * @var callable
                     */
                    private $func;
                    private $num;
                    private $pool;

                    public function __construct($num, $for, $func)
                    {
                        $this->num = $num;
                        $this->pool = range(1, $num);
                        $this->for = $for;
                        $this->func = $func;
                    }

                    public function run()
                    {
                        $for = $this->for;
                        foreach ($for() as $item) {
                            try {
                                while (true) {
                                    $co_id = array_pop($this->pool);
                                    if ($co_id) {
                                        $run_go = false;
                                        for ($i = 0; $i < 3; $i++) {
                                            $run_go = go(function () use ($item) {
                                                $func = $this->func;
                                                $func(...$item);
                                                $this->pool[] = 1;
                                            });
                                            if (!$run_go) {
                                                continue;
                                            } else {
                                                break;
                                            }
                                        }
                                        if (!$run_go) {
                                            Util::logW("run go Error continue next");
                                        }
                                        break;
                                    } else {
                                        Co::sleep(0.3);
                                    }
                                }
                            } catch (Throwable $e) {
                                var_dump($e);
                            }
                        }
                        while (true) {
                            if (count($this->pool) == $this->num) {
                                break;
                            }
                            Co::sleep(0.3);
                        }
                    }
                })->run();
                swoole_timer_clear($tid);
                $GLOBALS['process']->write("\e");
                $GLOBALS['process']->write("\e");
                $GLOBALS['process']->write("\e");
            });
        }, false, 2, "go__" . self::get_pid() . "_" . time());
        $pid = $process->start();
        sleep(1);
        $res = false;
        while (true) {
            $process->write("\t");
            sleep(1);
            if (!$content = $process->read()) {
                sleep(1);
                if (!$content = $process->read()) {
                    sleep(1);
                    if (!$content = $process->read()) {
                        Util::logW("co process is die");
                        SProcess::kill($pid, 9);
                        $res = false;
                        break;
                    }
                }
            } else {
                $arr = explode("\e", $content);
                if (count($arr) > 1) {
                    SProcess::kill($pid, 9);
                    $res = true;
                    break;
                } else {
                    continue;
                }
            }
        }
        return $res;
    }

    /**
     * @param $info
     * @param int $level
     * @return string
     */
    private static function makeInfo($info, int $level): string
    {
        if (is_array($info))
            $info = json_encode($info, JSON_UNESCAPED_UNICODE);
        else
            $info = (string) $info;

        $trace = debug_backtrace();
        if (isset($trace[$level])) $info = $info . ":::::" . substr($trace[$level]['file'], 17, 128) . '@' . $trace[$level]['line'];

        $tag = null;
//        $tag = SProcess::getLogTag();
        if ($tag) $info = "$tag # $info";

        return $info;
    }

    /**
     * @param $obj
     * @param string $tag
     * @param bool|null $echo
     */
    public static function logObj($obj, string $tag = "", bool $echo = null)
    {
        self::logD($tag . var_export($obj, true), 1, $echo);
    }

    /**
     * @param array $obj
     * @param string $tag
     */
    public static function exit(array $obj, string $tag = ""): void
    {
        self::logObj($obj, $tag);
        exit;
    }

    public static function logJsonType($obj, $tag = "", $type = null, int $level = null, bool $echo = null): void
    {
        $echo = $echo ?? true;
        $type = $type ?? "D";
        $level = $level ?? 0;

        $params = [$tag . json_encode($obj, JSON_UNESCAPED_UNICODE), $level + 1, $echo];

        switch (strtolower($type)) {
            default:
            case "d":
                self::logD(...$params);
                break;
            case "w":
                self::logW(...$params);
                break;
            case "f":
            case "e":
                self::logW(...$params);
                break;
            case "i":
                self::logI(...$params);
                break;
        }
    }

    public static function logJson($obj, string $tag = "", int $level = null, bool $echo = null): void
    {
        $echo = $echo ?? true;
        $level = $level ?? 0;

        self::logD($tag . json_encode($obj, JSON_UNESCAPED_UNICODE), $level + 1, $echo);
    }

    /**
     * @param $info
     * @param int $level
     * @param bool $echo
     */
    public static function logI($info, int $level = null, bool $echo = true): void
    {
        $level = $level ?? 0;
        if (!$info) return;
        $info = self::makeInfo($info, ++$level);
        var_dump($info);
//        LogHelper::logI($info);
        if (isset($GLOBALS['TEST']) && $GLOBALS['TEST'] && $echo) self::echoStr("INFO", $info);
    }

    private static function echoStr($tag, $str) {
        $uid = $_SERVER["HTTP_CONN_UID"] ?? 0;
        $date = date("Y-m-d H:i:s");
        echo "$tag($uid)[$date]: $str\n\n";
    }

    public static function logD($info = '', int $level = null, bool $echo = null)
    {
        $echo = $echo ?? true;
        $level = $level ?? 0;

        if (!$info) return;
        $info = self::makeInfo($info, ++$level);
        var_dump($info);
//        LogHelper::logD($info);
        if (isset($GLOBALS['TEST']) && $GLOBALS['TEST'] && $echo) self::echoStr("DEBUG", $info);
    }

    public static function logW($info = '', int $level = null, bool $echo = null)
    {
        $echo = $echo ?? true;
        $level = $level ?? 0;

        if (!$info) return;
        $info = self::makeInfo($info, ++$level);
        var_dump($info);
//        LogHelper::logW($info);
        if (isset($GLOBALS['TEST']) && $GLOBALS['TEST'] && $echo) self::echoStr("WARN", $info);
    }

    /**
     * @param $info
     * @param int $level
     * @param bool $echo
     */
    public static function logE($info, $level = null, bool $echo = null)
    {
        $echo = $echo ?? true;
        $level = $level ?? 0;

        if (!$info) return;
        $info = self::makeInfo($info, ++$level);

        if (!$info) return;
        if (is_array($info)) $info = json_encode($info, JSON_UNESCAPED_UNICODE);

        $tag = SProcess::getLogTag();
        if ($tag) $info = "$tag # $info";

        var_dump("ERROR", $info);
//        LogHelper::logW($info);
        if (isset($GLOBALS['TEST']) && $GLOBALS['TEST'] && $echo) self::echoStr("ERROR", $info);
    }

    /**
     * @param $obj
     * @return string
     */
    public static function dump($obj)
    {
        ob_start();
        if (is_array($obj)) {
            print_r($obj);
        } else {
            if (is_callable($obj))
                $obj();
            else
                self::logObj($obj);
        }
        $content = ob_get_contents();
        ob_end_clean();
        return "<pre>$content</pre>";
    }

    public static function getStringTime($val)
    {
        if (!$val) return "0000-00-00 00:00:00";
        if (is_numeric($val)) {
            if ($val > 1000064358000) {

                return date("Y-m-d H:i:s", (int)($val / 1000));
            } else {

                return date("Y-m-d H:i:s", (int)($val));
            }
        }
        if (strlen($val) === 10) return $val . " 00:00:00";
        if (strlen($val) !== 19) return "";
        return $val;
    }

    public static function getTimestamp($val)
    {
        if (!$val) return 0;
        if (is_numeric($val)) {
            if ($val > 1000064358000) {
                return (int)($val / 1000);
            } else {
                return (int)($val);
            }
        }
        if (strlen($val) === 10) return strtotime($val . " 00:00:00");
        if (strlen($val) === 8) return strtotime(date("Y-m-d ") . $val);
        if (strlen($val) !== 19) return null;
        return strtotime($val);
    }

    /**
     * @param string $path
     * @return Generator
     */
    public static function readLine(string $path): Generator
    {
        if (is_dir($path)) {
            $fileIterator = new FilesystemIterator($path);
            $path_arr = [];
            foreach ($fileIterator as $fileInfo) {
                if (!$fileInfo->isDir()) {
                    $path_arr[] = $fileInfo->getFileName();
                }
            }
            foreach ($path_arr as $path_item) {
                $file_path = $path . '/' . $path_item;
                Util::logD("dir-> $file_path");
                foreach (self::readLine($file_path) as $data) yield $data;
            }
        } else {
            if (!is_file($path))
                return;
            $fp = fopen($path, 'r');
            while (!feof($fp)) {
                $line = fgets($fp);
                if (!$line)
                    continue;
                $data = trim($line);
                if (!$data)
                    continue;
                yield $data;
            }
            fclose($fp);
        }
    }



    public static function readBuffer(string $path, int $num): Generator
    {
        $count = 0;
        $buffer = [];
        foreach (Util::readLine($path) as $line) {
            $buffer[] = $line;
            $count++;
            if ($count >= $num) {
                yield $buffer;
                $count = 0;
                $buffer = [];
            }
        }
        if ($count > 0) {
            yield $buffer;
        }
    }

    /**
     * @param $val
     * @return mixed
     * @throws ErrorException
     */
    public static function getArr($val)
    {
        try {
            if (is_string($val)) {
                return json_decode($val, true);
            } elseif (is_array($val)) {
                return $val;
            } else {
                throw new ErrorException('other situation');
            }
        } catch (Exception $e) {
            throw new ErrorException($e->getMessage());
        }
    }

    /**
     * @param $arr
     * @param $key
     * @return string
     */
    public static function hasArr($arr, $key)
    {
        return $arr[$key] ?? "";
    }

    /**
     * @param $key
     * @return null
     */
    public static function hasPost($key)
    {
        return $_POST[$key] ?? null;
    }

    /**
     * @param $key
     * @return null
     */
    public static function hasGet($key)
    {
        return $_GET[$key] ?? null;
    }

    /**
     * @param $url
     * @param null $data
     * @param null $agent
     * @return mixed
     */
    public static function curlPost($url, $data = null, $agent = null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        if ($agent) {
            curl_setopt($ch, CURLOPT_USERAGENT, $agent);
        }

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        if ($data) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($ch, CURLOPT_TIMEOUT, 3);

        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }


    /**
     * @param $url
     * @return resource
     */
    private static function initCurl($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        return $ch;
    }

    /**
     * @param int $uid
     * @return mixed
     */
    public static function isOnline(int $uid)
    {
        return conn_online_is_online($uid);
    }

    /**
     * @param $url
     * @return resource
     */
    private static function initSSLCurl($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        return $ch;
    }

    /**
     * @param $url
     * @param array $data
     * @param string $cookie
     * @param array $headers
     * @param array $setArr
     * @return mixed
     * @throws ErrorException
     */
//    public static function httpCurl(string $url, $data = [], string $cookie = '', array $headers = [], array $setArr = []): string
    public static function httpCurl($url, $data = [], $cookie = '', $headers = [], $setArr = [])
    {
        if (strpos($url, "http://") === 0) {
            $ch = self::initCurl($url);
        } elseif (strpos($url, "https://") === 0) {
            $ch = self::initSSLCurl($url);
        } elseif (strpos($url, "//") === 0) {
            $ch = self::initCurl("http:" . $url);
        } else {
            throw new ErrorException("internal error");
        }

        if ($data) {
//            if (is_array($data)) {
//                $data = http_build_query($data);
//            }
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        if ($cookie) {
//            $setting_arr[] = "X-Requested-With: XMLHttpRequest";
//            $setting_arr[] = "Cookie:PHPSESSID={$cookie}";
            curl_setopt($ch , CURLOPT_COOKIE , $cookie);
        }
        if ($headers) curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($setting_arr ?? [], $headers));
        if ($setArr) foreach ($setArr as $key => $setting) curl_setopt($ch, $key, $setting);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);

        $o = curl_exec($ch);
        curl_close($ch);
        return $o ? $o : '';
    }

    /**
     * @param int $count
     * @param string $url
     * @param array $data
     * @param string $cookie
     * @param string $referer
     * @return string
     */
    public static function httpCurlCoContinue(int $count, string $url, array $data = [], string $cookie = '', string $referer=''): string
    {
        while ($count > 0) {
            try {
                $option = [];
                if ($referer) {
                    $option['referer'] = $referer;
                }
                $result = (string)SaberGM::get(
                    $url, $option
                )->body;
            } catch (Throwable $e) {
                $result = false;
            }

            if (!$result) {
                $count--;
                Co::sleep(1);
            } else {
                return $result;
            }
        }
        return "";
    }

//    public static function httpCurlContinue(int $count, string $url, array $data = [], string $cookie = '', array $header = [], array $setArr = []): string
    public static function httpCurlContinue($count, $url, $data = [], $cookie = '', $header = [], $setArr = [])
    {
        while ($count > 0) {
            try {
                $res = self::httpCurl($url, $data, $cookie, $header, $setArr);
            } catch (ErrorException $e) {
                $res = "";
            }
            if (!$res) {
                $count--;
                sleep(1);
            } else {
                return $res;
            }
        }
        return "";
    }

    /**
     * @param $e
     * @return string
     */
    public static function err(Throwable $e)
    {
        return join('@', [$e->getFile(), $e->getLine(), $e->getMessage()]) . "\n";
    }

    /**
     * @param string $message
     * @param int $code
     * @return null
     * @throws ErrorException
     */
    public static function e(string $message = '', int $code = 0)
    {
        throw new ErrorException($message, $code);
    }

    public static function msectime()
    {
        list($msec, $sec) = explode(' ', microtime());
        $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);

        return $msectime;
    }

    /**
     * @return bool
     */
    public static function hostIsTest()
    {
        return false;
        $is_test_server = self::get_global('isInTestServer');

        if ($is_test_server === null) {
            $host_name = self::getHostName();
            if (
                stripos($host_name, 'osstest') === false &&
                stripos($host_name, 'test-server') === false &&
                stripos($host_name, 'wxtest') === false
            ) {
                self::set_global('isInTestServer', 0);
            } else {
                self::set_global('isInTestServer', 1);
            }
        }

        return !!self::get_global('isInTestServer');
    }

    /**
     * @return bool
     */
    public static function hostIsDebug()
    {
        $is_test_server = self::get_global('isInTestServer');

        if ($is_test_server === null) {
            $host_name = self::getHostName();
            if ($host_name === 'spider-test') {
                self::set_global('isInTestServer', 1);
            } else {
                if (stripos($host_name, 'doc') === false &&
                    stripos($host_name, 'osstest') === false &&
                    stripos($host_name, 'test') === false &&
                    stripos($host_name, 'wxtest') === false) {

                    if ($host_name == "conn-test") {
                        self::set_global('isInTestServer', 1);
                    } else {
                        self::set_global('isInTestServer', 0);
                    }
                } else {
                    self::set_global('isInTestServer', 1);
                }
            }
        }

        return !!self::get_global('isInTestServer');
    }

    public static function hostIsSpider() {
        $key_name = "isInSpiderServer";
        $is_test_server = self::get_global($key_name);

        if ($is_test_server === null) {
            $host_name = self::getHostName();
            if (Util::hasString($host_name, 'audio-spider')) {
                self::set_global($key_name, 1);
                return true;
            } else {
                self::set_global($key_name, 0);
                return false;
            }
        }

        return !!self::get_global($key_name);
    }

    public static function hostIsMgr() {
        $key_name = "isInMgrServer";
        $is_test_server = self::get_global($key_name);

        if ($is_test_server === null) {
            $host_name = self::getHostName();
            if (Util::hasString($host_name, 'conn-test')) {
                self::set_global($key_name, 1);
                return true;
            } else {
                self::set_global($key_name, 0);
                return false;
            }
        }

        return !!self::get_global($key_name);
    }

    /**
     * @return bool
     */
    public static function hostIsWeb()
    {
        $key_name = "isInWebServer";
        $is_test_server = self::get_global($key_name);

        if ($is_test_server === null) {
            $host_name = self::getHostName();
            if (Util::hasString($host_name, 'web-')) {
                self::set_global($key_name, 1);
                return true;
            } else {
                self::set_global($key_name, 0);
                return false;
            }
        }

        return !!self::get_global($key_name);
    }

    public static function hostIs205() {
        return self::getHostName() === "osstest.txzing.com";
    }

    public static function getHostName()
    {
        $host_name = self::get_global('host_name');
        if (!$host_name) {
            exec('uname -n', $arr);
            $host_name = $arr[0];
            self::set_global('host_name', $host_name);
        }

        return $host_name;
    }

    public static function getOssHost()
    {
        return (Util::hostIsTest() ? "http" : "https") . "://" . $_SERVER['HTTP_HOST'];
    }

    public static function getFileLine($path)
    {
        $fp = fopen($path, 'r');
        $line = 0;
        while (!feof($fp)) {
            //每次读取2M
            if ($data = fread($fp, 1024 * 1024 * 20)) {
                //计算读取到的行数
                $num = substr_count($data, "\n");
                $line += $num;
            }
        }
        fclose($fp);
        return $line;
    }

    public static function getNearLargerNumExpWith2Base($count)
    {
        $exp = 0;
        while (true) {
            $exp++;
            if ($count === 2) {
                break;
            }
            $count = intval(ceil($count * 0.5));
        }
        return $exp;
    }

    public static function reverseFile($old_path, $reverse_path)
    {
        $count = 0;
        $line_arr = [];
        $flag = 0;
        foreach (self::readLine($old_path) as $line) {
            $line_arr[] = $line;
            $count++;
            if ($count % 10000 === 0) {
                $line_arr = array_reverse($line_arr);
                $flag++;
                file_put_contents($reverse_path . '_' . $flag, join("\n", $line_arr));
                $line_arr = [];
            }
        }

        $line_arr = array_reverse($line_arr);
        $flag++;
        file_put_contents($reverse_path . '_' . $flag, join("\n", $line_arr));

        $fp = fopen($reverse_path, 'w');
        for ($i = $flag; $i > 0; --$i) {
            $tmp_file = $reverse_path . '_' . $i;
            fwrite($fp, file_get_contents($tmp_file));
            self::deleteFile($tmp_file);
        }
        fclose($fp);
    }

    public static function distinct($old_path, $new_path, $split = "$^", $key_index = 0)
    {
//        $line_count = self::getFileLine($old_path);
//        $exp = self::getNearLargerNumExpWith2Base($line_count);
//        $table = new swoole_table(pow(2, $exp));
//        $table->column('n', swoole_table::TYPE_STRING, 1);
//        $table->create();

        $root = dirname(__FILE__);

        $table = [];
        ini_set('memory_limit', '2048M');


        $reverse_path = "$root/" . base64_encode($old_path);
        self::reverseFile($old_path, $reverse_path);

        $tmp_new = $new_path . '_' . self::get_pid() . '_' . time();
        $fp = fopen($tmp_new, 'w');
        foreach (self::readLine($reverse_path) as $line) {
            $line_arr = explode($split, $line);
            $key_val = $line_arr[$key_index];
//            if (!$table->exist($key_val)) {
//                $table->set($key_val, [
//                    'n' => '1'
//                ]);
//                fwrite($fp, $line . "\n");
//            }
            if (!isset($table[$key_val])) {
                $table[$key_val] = true;
                fwrite($fp, $line . "\n");
            }
        }
        fclose($fp);
        self::deleteFile($reverse_path);
        self::reverseFile($tmp_new, $new_path);
        self::deleteFile($tmp_new);
    }

    /**
     * @param $string
     * @param $needle
     * @return bool|int
     */
    public static function getNeedlePos($string, $needle)
    {
        return strpos($string, $needle);
    }

    /**
     * @param $string
     * @param $needle
     * @return bool
     */
    public static function hasString($string, $needle)
    {
        if (!$string) return false;
        if (is_array($needle)) {
            foreach ($needle as $str) {
                if (is_numeric(strpos($string, $str))) {
                    return true;
                }
            }
            return false;
        } else {
            return is_numeric(strpos($string, $needle));
        }
    }

    /**
     * @param $str
     * @return mixed
     */
    public static function removeSpace($str)
    {
        return preg_replace('/\s*/', '', $str);
    }

    /**
     * @param $albumInfo
     * @param $obj
     * @return mixed
     */
    public static function changeToObj($albumInfo, $obj)
    {
        foreach ($albumInfo as $key => $val) {
            eval('$obj->' . $key . ' = $val;');
        }
        return $obj;
    }

    /**
     * @param $str
     * @param $parttern
     * @return bool
     */
    public static function startWith($str, $parttern)
    {
        if (!$str) return false;

        if (!is_string($str))
            Util::logW('is not string');

        if (!is_array($parttern)) {
            $parttern = [$parttern];
        }

        $flag = false;

        foreach ($parttern as $p) {
            if (strpos($str, $p) === 0) {
                $flag = true;
                break;
            }
        }

        return $flag;
    }

    /**
     * @param $str
     * @param $parttern
     * @return bool
     */
    public static function startWitsh($str, $parttern)
    {
        return self::startWith($str, $parttern);
    }

    public static function main()
    {
        $trace_arr = debug_backtrace();
        $trace = $trace_arr[0] ?? [];
        $file = $trace['file'] ?? false;
        if (!$file) {
            return false;
        }
        return stat($_SERVER['SCRIPT_FILENAME']) === stat($file);
    }

    /**
     * @param $str
     * @return mixed
     */
    public static function getJsonStr($str)
    {
        if (is_bool($str) || !$str) {
            return null;
        }
        $json = '';
        $first_pos = strpos($str, "{");
        $last_pos = strripos($str, "}");
        $diff = $last_pos - $first_pos;
        if ($diff > 3) {
            $json = substr($str, $first_pos, $diff + 1);
        }
        return $json;
    }

    /**
     * @param $str
     * @return mixed
     */
    public static function getJsonData($str)
    {
        $data_str = self::getJsonStr($str);
        if (!$data_str) {
            return false;
        }
        try {
            return json_decode($data_str, true);
        } catch (Throwable $e) {
            return false;
        }
    }

    /**
     * @param $image_file
     * @return string
     */
    public static function base64EncodeImage($image_file)
    {
        $image_info = getimagesize($image_file);
        $image_data = fread(fopen($image_file, 'r'), filesize($image_file));
        $base64_image = 'data:' . $image_info['mime'] . ';base64,' . chunk_split(base64_encode($image_data));
        return $base64_image;
    }

    /**
     * @param $str
     */
    public static function println($str)
    {
        if (!is_array($str)) {
            $str = [$str];
        }
        foreach ($str as $line) {
            if (is_array($line)) {
                echo json_encode($line, JSON_UNESCAPED_UNICODE) . "\n";
            } else {
                echo $line . "\n";
            }
        }
    }

    /**
     * @param $name
     * @return int
     */
    public static function strWithChinese($name)
    {
        if (preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $name) > 0) {
            return 2;
        } elseif (preg_match('/[\x{4e00}-\x{9fa5}]/u', $name) > 0) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * @param $file
     */
    public static function deleteFile($file)
    {
        if (is_file($file)) {
            unlink($file);
        }
    }

    public static function swoftInterFaceCall(string $interface, string $version, string $method, array $params = [],
                                              $need_result = true)
    {
        return;
        if (Util::hostIsTest()) {
            return null;
        }

        if ($interface === 'RecommendInterface') {
            $host = "172.16.48.160:24689";
        } else {
            $host = "172.16.48.160:24689";
        }

        $fp = stream_socket_client("tcp://$host", $err_no, $err_str);
        if (!$fp) {
            Util::logW("stream_socket_client fail err_no={$err_no} err_str={$err_str}");
            return null;
        }
        $data = [
            'interface' => "App\Lib\\$interface",
            'version' => $version ?: 0,
            'method' => $method,
            'params' => $params,
            'logid' => uniqid(),
            'spanid' => 0,
        ];
        $data = json_encode($data, JSON_UNESCAPED_UNICODE) . "\n";
        fwrite($fp, $data);
        if (!$need_result) {
            fclose($fp);
            return null;
        }

        $result = fread($fp, 1024);
        fclose($fp);
        return $result;
    }

    public static function fixUrl($url)
    {
        if (!$url) return null;
        if (self::startWith($url, 'http://')) return $url;
        if (self::startWith($url, '//')) return "http:$url";
        if (self::startWith($url, '://')) return "http$url";
        return $url;
    }

    public static function DictFilter($spider_dict, $db_dict, &$update, &$add) {
        foreach ($spider_dict as $key=>$v) {
            if (isset($db_dict[$key])) {
                $update[] = $key;
            } else {
                $add[] = $key;
            }
        }
    }
}
